package com.gitee.wsl.mathematics.function.equation.ext

import com.gitee.wsl.mathematics.matrix.MutableMatrix

/**
 * Transform to row echelon matrix using elementary transformation,
 * and apply this transformation to [other]
 * @return impact on its determinate value
 *
 * 原地通过行初等变换变为行阶梯型阵
 * 对 [other] 应用同样变换
 * @return 变换导致行列式值的变化的倍数
 */
fun<T:Number> MutableMatrix<T, *, *>.rowEchelonAssignWith(
    other: Iterable<MutableMatrix<T, *, *>>
): Double {
    // 对接近零的数字规范化
    proxy.update { if (it.isApproxZero()) zero else it }
    // 记录交换次数
    var sum = 0
    // 固定行数
    var fixed = 0
    // 按列化简
    for (c in 0 until colCount) {
        // 从此列所有未固定的行中找到第一个非零元素，全为零则直接查找下一列
        val i = (fixed until rowCount).firstOrNull { r -> get(r, c) != .0 } ?: continue
        // 将其所在行交换到未固定的首行
        if (i != fixed) {
            ++sum
            exchangeRow(i, fixed)
            other.forEach { it.exchangeRow(i, fixed) }
        }

        // 取出这一行的首元
        val head = get(fixed, c)
        // 用首元将此列未固定的其他元素化为零
        for (r in fixed + 1 until rowCount) {
            val k = -get(r, c) / head
            plusToRow(k, fixed, r)
            other.forEach { it.plusToRow(k, fixed, r) }
            // 强保证化为零有效
            set(r, c, zero)
        }

        // 固定行数加一
        ++fixed
    }
    return if (sum % 2 == 0) 1.0 else -1.0
}

/**
 * Transform to the simplest row echelon matrix using elementary transformation,
 * and apply this transformation to [other]
 * @return impact on its determinate value
 *
 * 原地通过行初等变换变为最简行阶梯型阵
 * 对 [other] 应用同样变换
 * @return 变换导致行列式值的变化的倍数
 */
fun<T:Number> MutableMatrix<T, *, *>.simplifyAssignWith(
    other: Iterable<MutableMatrix<T, *, *>>
): Double {
    var scale = rowEchelonAssignWith(other)

    var c = colCount
    // 清零上三角区
    for (r in (0 until rowCount).reversed()) {
        // 找到本行最后一个非零元素
        var tail: Double
        do {
            tail = get(r, --c).toDouble()
        } while (c >= 0 && tail == .0)

        // 在此进行 0 约化会导致 40 阶希尔伯特矩阵求逆的测试无法通过
        // do {
        //     tail = get(r, --c)
        //     if (!doubleEquals(tail, .0)) break
        //     // 接近 0 视作 0
        //     set(r, c, .0)
        // } while (c >= 0)

        // 有全零行，不再继续化简
        if (c < 0) break

        // 用首元将此列未固定的其他元素化为零
        for (t in (0 until r)) {
            val k = -get(t, c) / tail
            plusToRow(k, r, t)
            other.forEach { it.plusToRow(k, r, t) }
            // 强保证化为零有效
            set(t, c, zero)
        }
    }

    c = 0
    // 主对角线归一化
    for (r in 0 until rowCount)
        while (c < colCount) {
            val item = get(r, c++)
            if (item.isApproxZero()) {
                set(r, c - 1, zero)
                continue
            }

            val k = 1 / item
            scale = (scale * k).toDouble()
            timesRow(r, k)
            other.forEach { it.timesRow(r, k) }
            break
        }

    return scale
}


/**
 * Transform to row echelon matrix using elementary transformation,
 * and apply this transformation to [other]
 * @return impact on its determinate value
 *
 * 原地通过行初等变换变为行阶梯型阵
 * 对 [other] 应用同样变换
 * @return 变换导致行列式值的变化的倍数
 */
fun<T:Number> MutableMatrix<T, *, *>.rowEchelonAssignWith(
    vararg other: MutableMatrix<T, *, *>
) = rowEchelonAssignWith(other.toList())

/**
 * Transform to the simplest row echelon matrix using elementary transformation,
 * and apply this transformation to [other]
 * @return impact on its determinate value
 *
 * 原地通过行初等变换变为最简行阶梯型阵
 * 对 [other] 应用同样变换
 * @return 变换导致行列式值的变化的倍数
 */
fun<T:Number> MutableMatrix<T, *, *>.simplifyAssignWith(
    vararg other: MutableMatrix<T, *, *>
) = simplifyAssignWith(other.toList())

/**
 * Transform to row echelon matrix using elementary transformation
 * @return impact on its determinate value
 *
 * 原地通过行初等变换变为行阶梯型阵
 * @return 变换导致行列式值的变化的倍数
 */
fun<T:Number> MutableMatrix<T, *, *>.rowEchelonAssign() = rowEchelonAssignWith()

/**
 * Transform to the simplest row echelon matrix using elementary transformation
 * @return impact on its determinate value
 *
 * 原地通过行初等变换变为最简行阶梯型阵
 * @return 变换导致行列式值的变化的倍数
 */
fun<T:Number> MutableMatrix<T, *, *>.simplifyAssign() = simplifyAssignWith()
